#include "script/glue/LuaGlue.h"

#include <OgreVector3.h>
#include "core/Value.h"

namespace LuaGlue
{

	int Vector3::tostring (lua_State *L)
	{
		std::stringstream ss;
		switch(lua_gettop(L))
			{
			case 0:
				lua_pushstring(L, className);
				break;
			case 1:
				ss << *unwrap<Ogre::Vector3>(L, 1);
				lua_pushstring(L, ss.str().c_str());
				break;
			default:
				return 0;
				break;
			}
		return 1;
	}

	int Vector3::create(lua_State *L)
	{
		Ogre::Vector3 *v = NULL;
		double x,y,z;

		switch(lua_gettop(L))
			{
			case 0:
				x = y = z = 0;
				break;
			case 1:
				if(!lua_istable(L, 1))
					{
						luaL_error(L, "Wrong type for parameter, expected a table, got %s", lua_typename(L,1) );
						return 0;
					}

				lua_pushinteger(L, 1);
				lua_gettable(L, 1);
				x = lua_tonumber(L, -1);
				lua_pop(L, 1);

				lua_pushinteger(L, 2);
				lua_gettable(L, 1);
				y = lua_tonumber(L, -1);
				lua_pop(L, 1);

				lua_pushinteger(L, 3);
				lua_gettable(L, 1);
				z = lua_tonumber(L, -1);
				lua_pop(L, 1);

				break;
			case 3:
				x = luaL_checknumber(L, 1);
				y = luaL_checknumber(L, 2);
				z = luaL_checknumber(L, 3);
				break;
			default:
				luaL_error(L, "Incorrect number of arguments for function");
				return 0;
			}

		v = new Ogre::Vector3(x,y,z);

		Wrapper<Vector3>::Instance(L, (void*)v);

		return 1;
	}

	int Vector3::get( lua_State *L )
	{
		Ogre::Vector3* v = unwrap<Ogre::Vector3>(L, 1);

		const char *key = luaL_checkstring(L, 2);

		if(Wrapper<Vector3>::Method(L, key)) return 1;

		if (strcmp(key, "x") == 0)
			lua_pushnumber(L, v->x);
		else if (strcmp(key, "y") == 0)
			lua_pushnumber(L, v->y);
		else if (strcmp(key, "z") == 0)
			lua_pushnumber(L, v->z);

		return 1;
	}

	int Vector3::set( lua_State *L )
	{
		Ogre::Vector3* v = unwrap<Ogre::Vector3>(L, 1);

		const char *key = luaL_checkstring(L, 2);

		if(lua_isnumber(L,3))
			{
				float val = luaL_checknumber (L, 3);

				if (strcmp(key, "x") == 0)
					v->x= val;
				else if (strcmp(key, "y") == 0)
					v->y= val;
				else if (strcmp(key, "z") == 0)
					v->z= val;
			}

		return 1;
	}

	int Vector3::add(lua_State *L)
	{
		int nargs = lua_gettop(L);
		if(nargs<=1 || nargs>=3)
			{
				luaL_error(L, "Wrong number of parameters");
				return 0;
			}

		Ogre::Vector3 *u = unwrap<Ogre::Vector3>(L, 1);
		Ogre::Vector3 *v = unwrap<Ogre::Vector3>(L, 2);

		if(!u || u->isNaN()) return 0;
		if(!v || v->isNaN()) return 0;

		Ogre::Vector3 *w = new Ogre::Vector3( (*u) + (*v) );

		if(w->isNaN())
			{
				lua_pushnil(L);
				delete w;
				return 0;
			}

		Wrapper<Vector3>::Instance(L, (void*)w);

		return 1;
	}

	int Vector3::sub(lua_State *L)
	{
		int nargs = lua_gettop(L);
		if(nargs<=1 || nargs>=3)
			{
				luaL_error(L, "Wrong number of parameters");
				return 0;
			}

		Ogre::Vector3 *u = unwrap<Ogre::Vector3>(L, 1);
		Ogre::Vector3 *v = unwrap<Ogre::Vector3>(L, 2);

		if(!u || u->isNaN()) return 0;
		if(!v || v->isNaN()) return 0;

		Ogre::Vector3 *w = new Ogre::Vector3( (*u) - (*v) );

		if(w->isNaN())
			{
				lua_pushnil(L);
				delete w;
				return 0;
			}

		Wrapper<Vector3>::Instance(L, (void*)w);

		return 1;
	}


	int Vector3::mul(lua_State *L)
	{
		Ogre::Vector3 *u = unwrap<Ogre::Vector3>(L, 1);
		if(!u || u->isNaN()) return 0;
		if(u->isZeroLength())
			{
				Wrapper<Vector3>::Instance(L, (void*)u);
				return 1;
			}

		switch(lua_type(L, 2))
			{
			case LUA_TNUMBER:
			{
				float s = luaL_checknumber(L, 2);
				Ogre::Vector3 *w = new Ogre::Vector3( (*u) * s );
//				if(w->isNaN())
//					{
//						lua_pushnil(L);
//						delete w;
//						return 0;
//					}

				Wrapper<Vector3>::Instance(L, (void*)w);
			}
			break;

			case LUA_TTABLE:
			{
				Ogre::Vector3 *v = unwrap<Ogre::Vector3>(L, 2);

				if(!u || u->isNaN()) return 0;
				if(!v || v->isNaN()) return 0;

				Ogre::Vector3 *w = new Ogre::Vector3( (*u) * (*v) );
				if(!w || w->isNaN())
					{
						lua_pushnil(L);
						delete w;
						return 0;
					}

				Wrapper<Vector3>::Instance(L, (void*)w);
			}
			break;
			default:
				luaL_error(L, "Wrong type for parameter, expected a Vector3 or a scalar (number), got %s", lua_typename(L,2) );
				break;
			}

		return 1;
	}

//	int Vector3::div(lua_State *L)
//	{
//		Ogre::Vector3 *u = unwrap<Ogre::Vector3>(L, 1);
//		if(!u || u->isNaN()) return 0;
//		if(u->isZeroLength())
//			{
//				lua_pushnil(L);
//				return 1;
//			}
//		Ogre::Vector3 *v = unwrap<Ogre::Vector3>(L, 2);
//
//		if(!u || u->isNaN()) return 0;
//		if(!v || v->isNaN()) return 0;
//
//		Ogre::Vector3 *w = new Ogre::Vector3( (*u) / (*v) );
//
//		if(w->isNaN())
//			{
//				lua_pushnil(L);
//				delete w;
//				return 0;
//			}
//
//		Wrapper<Vector3>::Instance(L, (void*)w);
//
//		return 1;
//	}

	int Vector3::length(lua_State *L)
	{
		lua_pushnumber(L, unwrap<Ogre::Vector3>(L, 1)->length());
		return 1;
	}

	int Vector3::normalise(lua_State *L)
	{
		lua_pushnumber(L, unwrap<Ogre::Vector3>(L, 1)->normalise());
		return 1;
	}
	
	int Vector3::getRotationTo(lua_State *L)
	{
		Ogre::Vector3* v = unwrap<Ogre::Vector3>(L, 1);
		Ogre::Vector3* u = unwrap<Ogre::Vector3>(L, 2);
		Ogre::Quaternion* q = new Ogre::Quaternion(v->getRotationTo(*u));
		Wrapper<Quaternion>::Instance(L, (void*)q);
		return 1;
	}
}

DECLARE(Vector3)
META(Vector3)
{"__index",    LuaGlue::Vector3::get},
{"__newindex", LuaGlue::Vector3::set},
{"__tostring", LuaGlue::Vector3::tostring},
{"__add", 	   LuaGlue::Vector3::add},
{"__sub", 	   LuaGlue::Vector3::sub},
{"__mul", 	   LuaGlue::Vector3::mul},
//{"__div", 	   LuaGlue::Vector3::div},
METHODS(Vector3)
method(LuaGlue::Vector3, add),
method(LuaGlue::Vector3, sub),
method(LuaGlue::Vector3, mul),
method(LuaGlue::Vector3, tostring),
method(LuaGlue::Vector3, length),
method(LuaGlue::Vector3, normalise),
method(LuaGlue::Vector3, getRotationTo),
//method(LuaGlue::Vector3, sub),
//method(LuaGlue::Vector3, div),
END
